﻿using System;

namespace DataStructures.List
{
    public class ArrayList<E> : AbstractList<E>
    {
        protected int capacityIncrement;
        protected int size;
        protected E[] array;

        public ArrayList() : this(32) { }

        public ArrayList(int initialCapacity) : this(initialCapacity, 32) { }

        public ArrayList(int initialCapacity, int capacityIncrement)
        {
            if (initialCapacity <= 0)
            {
                throw new ArgumentOutOfRangeException("initialCapacity",
                    "The initial capacity must be positive");
            }
            if (capacityIncrement <= 0)
            {
                throw new ArgumentOutOfRangeException("capacityIncrement",
                    "The increment of the capacity must be positive");
            }
            this.size = 0;
            this.array = new E[initialCapacity];
            this.capacityIncrement = capacityIncrement;
        }

        public override int Size
        {
            get
            {
                return size;
            }
        }

        public override E getByIndex(int index)
        {
            checkEmpty();
            return array[index];
        }

        public override bool insertByIndex(int index, E element)
        {
            if (index > size)
            {
                throw new IndexOutOfRangeException(
                    "The index cannot be larger than the size of the list");
            }
            int i = size;
            if (size == array.Length)
            {
                E[] newArray = new E[array.Length + capacityIncrement];
                while (i > index)
                {
                    int j = i - 1;
                    newArray[i] = array[j];
                    i = j;
                }
                newArray[index] = element;
                while (i > 0)
                {
                    newArray[--i] = array[i];
                }
                array = newArray;
            }
            else // size < array.Length
            {
                while (i > index)
                {
                    int j = i - 1;
                    array[i] = array[j];
                    i = j;
                }
                array[index] = element;
            }
            ++size;
            return true;
        }

        public override E removeByIndex(int index)
        {
            checkEmpty();
            checkIndexMax(index);
            E temp = array[index];
            for (int i = index, j; (j = i + 1) < size; i = j)
            {
                array[i] = array[j];
            }
            --size;
            return temp;
        }

        public override bool setByIndex(int index, E element)
        {
            checkEmpty();
            checkIndexMax(index);
            array[index] = element;
            return true;
        }

        public override bool push(E element)
        {
            return insertAfterLast(element);
        }
        public override E pop()
        {
            return removeLast();
        }

        public override E getTop()
        {
            return getLast();
        }
        public override bool setTop(E element)
        {
            return setFirst(element);
        }

        protected class ArrayListIterator<E> : IDoublyIterator<E>
        {
            private int currentIndex, size;
            private E[] array;

            public ArrayListIterator(int size, E[] array)
            {
                this.size = size;
                this.array = array;
                this.currentIndex = 0;
            }

            public bool hasNext()
            {
                return currentIndex < size;
            }

            public E next()
            {
                if (!hasNext()) throw new IndexOutOfRangeException();
                E temp = array[currentIndex++];
                return temp;
            }

            public bool hasPrev()
            {
                return currentIndex > 0;
            }

            public E prev()
            {
                if (!hasPrev()) throw new IndexOutOfRangeException();
                return array[--currentIndex];
            }
        }

        public override IIterator<E> getIterator()
        {
            return new ArrayListIterator<E>(size, array);
        }

    }
}
